package co.fitstart.coach.service.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.utils.CollectionUtils;

import co.fitstart.coach.dao.CourseDao;
import co.fitstart.coach.dao.CoursewareDao;
import co.fitstart.coach.dao.CoursewareTemplateDao;
import co.fitstart.coach.entity.courseware.BatchDietProgram;
import co.fitstart.coach.entity.courseware.MovementSearch;
import co.fitstart.coach.handler.SmsSender;
import co.fitstart.coach.service.CoursewareService;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.courseware.CoursewareTag;
import co.fitstart.entity.courseware.FitnessTag;
import co.fitstart.entity.courseware.dietary.DietaryProgram;
import co.fitstart.entity.courseware.dietary.DietaryProgramTemplate;
import co.fitstart.entity.courseware.dietary.DietaryProgramType;
import co.fitstart.entity.courseware.exercise.ExerciseContent;
import co.fitstart.entity.courseware.fitnessgroup.BatchCourseware;
import co.fitstart.entity.courseware.fitnessgroup.BatchCoursewareTemplate;
import co.fitstart.entity.courseware.fitnessgroup.BatchCoursewareType;
import co.fitstart.entity.courseware.fitnessgroup.Courseware;
import co.fitstart.entity.courseware.fitnessgroup.CoursewareMovementType;
import co.fitstart.entity.courseware.fitnessgroup.CoursewareTemplate;
import co.fitstart.entity.courseware.fitnessgroup.DailyCourseware;
import co.fitstart.entity.courseware.fitnessgroup.DailyCoursewareTemplate;
import co.fitstart.entity.courseware.fitnessgroup.DailyCoursewareType;
import co.fitstart.entity.courseware.fitnessgroup.FitnessGroup;
import co.fitstart.entity.courseware.fitnessgroup.SpecialCoursewareTemplate;
import co.fitstart.entity.courseware.movement.Movement;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.user.Student;

@Service("coursewareService")
public class CoursewareServiceImpl implements CoursewareService{
    
    @Dynamic
    private CoursewareDao coursewareDao;
    
    @Dynamic
    private CoursewareTemplateDao templateDao;
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Autowired
    private SmsSender smsSender;
    
    @Dynamic
    private CourseDao courseDao;
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.GroupService#loadBatchCourseware(java.lang.Long)
     */
    public BatchCourseware loadBatchCourseware(Long batchId) {
        return genericDaoSupport.load(BatchCourseware.class, batchId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#getBatchCourseware(co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.courseware.fitnessgroup.BatchCoursewareType)
     */
    public BatchCourseware getBatchCourseware(CourseRound courseRound, BatchCoursewareType type) {
        BatchCourseware batchCourseware = coursewareDao.getBatchCourseware(courseRound, type, 0, 1);
        
        if(batchCourseware == null) {
            batchCourseware = this.getBatchCourseware(courseRound, type, 1);
        }
        
        return batchCourseware;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#getBatchCourseware(co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.courseware.fitnessgroup.BatchCoursewareType, int)
     */
    public BatchCourseware getBatchCourseware(CourseRound courseRound, BatchCoursewareType type, int sorting) {
        return coursewareDao.getBatchCourseware(courseRound, type, sorting);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#getDailyCourseware(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public DailyCourseware getDailyCourseware(BatchCourseware batchCourseware) {
        DailyCourseware dailyCourseware = coursewareDao.getDailyCourseware(batchCourseware, 0, 1);
        
        if(dailyCourseware == null) {
            dailyCourseware = this.getDailyCourseware(batchCourseware, 1);
        }
        
        return dailyCourseware;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#getDailyCourseware(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, int)
     */
    public DailyCourseware getDailyCourseware(BatchCourseware batchCourseware, int sorting) {
        return coursewareDao.getDailyCourseware(batchCourseware, sorting);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#loadFitnessGroup(java.lang.Long)
     */
    public FitnessGroup loadFitnessGroup(Long fitnessGroupId) {
        return genericDaoSupport.load(FitnessGroup.class, fitnessGroupId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#listCoursewareTag(java.util.List)
     */
    public List<CoursewareTag> listCoursewareTag(List<Long> tagIds) {
        return coursewareDao.listCoursewareTag(tagIds);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.GroupService#loadDailyCourseware(java.lang.Long)
     */
    public DailyCourseware loadDailyCourseware(Long dailyId) {
        return genericDaoSupport.load(DailyCourseware.class, dailyId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.GroupService#loadCourseware(java.lang.Long)
     */
    public Courseware loadCourseware(Long coursewareId) {
        return genericDaoSupport.load(Courseware.class, coursewareId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#listDietaryProgramTemplate()
     */
    public Map<String, List<DietaryProgramTemplate>> listDietaryProgramTemplate() {
        List<DietaryProgramTemplate> templates = templateDao.listDietaryProgramTemplate();
        return CollectionUtils.constructMapValuesFromList(templates, "type.alias");
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#loadDietaryProgramTemplate(java.lang.Long)
     */
    public DietaryProgramTemplate loadDietaryProgramTemplate(Long templateId) {
        return genericDaoSupport.load(DietaryProgramTemplate.class, templateId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#loadDietaryProgram(java.lang.Long)
     */
    public DietaryProgram loadDietaryProgram(Long dietaryProgramId) {
        return genericDaoSupport.load(DietaryProgram.class, dietaryProgramId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseRoundService#changeGroup(co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.courseware.fitnessgroup.FitnessGroup, java.lang.String)
     */
    public void changeGroup(CourseRound courseRound, FitnessGroup fitnessGroup, String tagIds) {
        // change courseRound's fitnessGroup
        courseRound.addAttribute("courseware_weekly_amount", fitnessGroup.getWeeks());
        if(!courseRound.getCourseType().isOnline() && !(courseRound.getCourseType().isIndividual() && !courseRound.getCourse().isExperience())) {
            courseRound.addAttribute("courseware_course_amount", fitnessGroup.getHours());
        }
        genericDaoSupport.update(courseRound.changeGroup(fitnessGroup));
        
        // create batchCourseware, dailyCourseware, courseware
        this.createBatchCourseware(fitnessGroup, courseRound);
        
        // change student's fitnessTag
        if(StringUtils.isNoneEmpty(tagIds)) {
            this.createTags(courseRound.getStudent(), tagIds);
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#editGroup(co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.courseware.fitnessgroup.FitnessGroup)
     */
    public void editGroup(CourseRound courseRound, FitnessGroup fitnessGroup) {
        // change courseRound's fitnessGroup
        genericDaoSupport.update(courseRound.changeGroup(fitnessGroup));
        
        this.updataBatchCourseware(courseRound, fitnessGroup);
    }
    
    /**
     * 
     * @param courseRound
     * @param fitnessGroup
     */
    private void updataBatchCourseware(CourseRound courseRound, FitnessGroup fitnessGroup) {
        // get unFinished batch
        List<BatchCourseware> batchCoursewares = coursewareDao.listBatchCourseware(courseRound);
        
        // get batchCoursewareTemplate
        List<BatchCoursewareTemplate> batchCoursewareTemplates = templateDao.listBatchCoursewareTemplate(fitnessGroup);
        
        for(BatchCourseware batchCourseware : batchCoursewares) {
            for(BatchCoursewareTemplate batchCoursewareTemplate : batchCoursewareTemplates) {
                if(batchCourseware.getType().equals(batchCoursewareTemplate.getType()) && batchCourseware.getSorting() == batchCoursewareTemplate.getSorting()) {
                    genericDaoSupport.update(batchCourseware.edit(batchCoursewareTemplate));
                    
                    // update dietaryPrograms
                    this.updateDietaryPrograms(batchCourseware, batchCoursewareTemplate);
                    
                    // update dailyCourseware
                    this.updateDailyCourseware(batchCourseware, batchCoursewareTemplate);
                    
                }
            }
            
        }
        
        if(!batchCoursewares.isEmpty() && courseRound.getCourseType().isIndividual() && !courseRound.getCourse().isExperience()) {
            coursewareDao.disableDietary(batchCoursewares);
        }
    }
    
    /**
     * 
     * @param batchCourseware
     * @param batchCoursewareTemplate
     */
    private void updateDietaryPrograms(BatchCourseware batchCourseware, BatchCoursewareTemplate batchCoursewareTemplate) {
        if(batchCoursewareTemplate.getBreakfast() != null) {
            this.updateDietaryProgram(batchCourseware.getDietaryPrograms().get(DietaryProgramType.BREAKFAST), batchCoursewareTemplate.getBreakfast(), batchCourseware);
        }
        
        if(batchCoursewareTemplate.getLunch() != null) {
            this.updateDietaryProgram(batchCourseware.getDietaryPrograms().get(DietaryProgramType.LUNCH), batchCoursewareTemplate.getLunch(), batchCourseware);
        }
        
        if(batchCoursewareTemplate.getDinner() != null) {
            this.updateDietaryProgram(batchCourseware.getDietaryPrograms().get(DietaryProgramType.DINNER), batchCoursewareTemplate.getDinner(), batchCourseware);
        }
    }
    
    /**
     * 
     * @param dietaryProgram
     * @param template
     */
    private void updateDietaryProgram(DietaryProgram dietaryProgram, DietaryProgramTemplate template, BatchCourseware batchCourseware) {
        if(dietaryProgram == null) {
            DietaryProgram snacks = null;
            if(template.getSnacks() != null) {
                DietaryProgramTemplate templateSnacks = genericDaoSupport.load(DietaryProgramTemplate.class, template.getSnacks());
                snacks = new DietaryProgram(templateSnacks, batchCourseware);
                genericDaoSupport.save(snacks);
            }
            
            DietaryProgram newDiet = new DietaryProgram(template, batchCourseware, snacks == null ? null : snacks.getId());
            genericDaoSupport.save(newDiet);
        } else {
            
            // update dietaryProgram's snacks first
            if(template.getSnacks() != null) { // if template has snacks edit dietaryProgram's snacks
                DietaryProgramTemplate snacksTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, template.getSnacks());
                if(dietaryProgram.getSnacks() == null) {
                    DietaryProgram snacks = new DietaryProgram(template, dietaryProgram.getBatch());
                    genericDaoSupport.save(snacks);
                    
                    dietaryProgram.editSnacks(snacks.getId());
                } else {
                    DietaryProgram snacks = genericDaoSupport.load(dietaryProgram.getClass(), dietaryProgram.getSnacks());
                    genericDaoSupport.update(snacks.edit(snacksTemplate));
                }
            } else { // if template doesn't has snacks set dietaryProgram's snacks to null
                if(dietaryProgram.getSnacks() != null) {
                    DietaryProgram snacks = genericDaoSupport.load(dietaryProgram.getClass(), dietaryProgram.getSnacks());
                    genericDaoSupport.update(snacks.disable());
                    
                    dietaryProgram.editSnacks(null);
                }
            }
            
            genericDaoSupport.update(dietaryProgram.edit(template));
        }
    }
    
    /**
     * 
     * @param batchCourseware
     * @param batchCoursewareTemplate
     */
    private void updateDailyCourseware(BatchCourseware batchCourseware, BatchCoursewareTemplate batchCoursewareTemplate) {
        List<DailyCourseware> dailyCoursewares = coursewareDao.listDailyCourseware(batchCourseware);
        List<DailyCoursewareTemplate> dailyCoursewareTemplates = templateDao.listDailyCoursewareTemplate(batchCoursewareTemplate);
        for (DailyCourseware dailyCourseware : dailyCoursewares) {
            for(DailyCoursewareTemplate dailyCoursewareTemplate : dailyCoursewareTemplates) {
                if(dailyCourseware.getSorting() == dailyCoursewareTemplate.getSorting()) {
                    genericDaoSupport.update(dailyCourseware.edit(dailyCoursewareTemplate));
                    
                    // delete original courseware first
                    coursewareDao.deleteCourseware(dailyCourseware);
                    this.createCourseware(dailyCoursewareTemplate, dailyCourseware);
                }
            }
        }
    }
    
    /**
     * 
     * @param student
     * @param tagIds
     */
    private void createTags(Student student, String tagIds) {
        String tags = "";
        List<FitnessTag> fitnessTags = coursewareDao.listFitnessTag(this.getArrayTagIds(tagIds));
        for(FitnessTag fitnessTag : fitnessTags) {
            CoursewareTag coursewareTag = new CoursewareTag(fitnessTag);
            Long key = (Long) genericDaoSupport.save(coursewareTag);
            tags = tags + "," + key.toString();
        }
        
        genericDaoSupport.update(student.editTag(tags));
    }
    
    /**
     * 
     * @param tagIds
     * @return
     */
    private List<Long> getArrayTagIds(String tagIds) {
        List<Long> arrayTagIds = new ArrayList<>();
        if(tagIds != null) {
            for(String tagId : StringUtils.split(tagIds, ",")) {
                arrayTagIds.add(Long.parseLong(tagId));
            }
        }
        return arrayTagIds;
    }
    
    /**
     * 
     * @param fitnessGroup
     * @param courseRound
     * @return
     */
    private void createBatchCourseware(FitnessGroup fitnessGroup, CourseRound courseRound) {
        List<BatchCoursewareTemplate> batchCoursewareTemplates = templateDao.listBatchCoursewareTemplate(fitnessGroup);
        
        for(BatchCoursewareTemplate batchCoursewareTemplate : batchCoursewareTemplates) {
            if(!courseRound.getCourseType().isIndividual() || courseRound.getCourse().isExperience() || batchCoursewareTemplate.getType().isWeekly()) {
                BatchCourseware batchCourseware = new BatchCourseware(batchCoursewareTemplate, courseRound);
                genericDaoSupport.save(batchCourseware);
                
                // create dietaryProgram
                this.createDietaryPrograms(batchCoursewareTemplate, batchCourseware);
                
                if(!courseRound.getCourseType().isIndividual() || courseRound.getCourse().isExperience()) {
                    // create dailyCourseware
                    this.createDailyCourseware(batchCoursewareTemplate, batchCourseware);
                }
            }
        }
    }
    
    /**
     * 
     * @param batchCoursewareTemplate
     * @param batchCourseware
     */
    private void createDailyCourseware(BatchCoursewareTemplate batchCoursewareTemplate, BatchCourseware batchCourseware) {
        List<DailyCoursewareTemplate> dailyCoursewareTemplates = templateDao.listDailyCoursewareTemplate(batchCoursewareTemplate);
        
        for(DailyCoursewareTemplate template : dailyCoursewareTemplates) {
            DailyCourseware dailyCourseware = new DailyCourseware(template, batchCourseware);
            genericDaoSupport.save(dailyCourseware);
            
            // create courseware
            this.createCourseware(template, dailyCourseware);
        }
    }
    
    /**
     * 
     * @param batchCoursewareTemplate
     * @param batchCourseware
     */
    private void createDietaryPrograms(BatchCoursewareTemplate batchCoursewareTemplate, BatchCourseware batchCourseware) {
        if(batchCoursewareTemplate.getBreakfast() != null) {
            this.createDietaryProgram(batchCourseware, batchCoursewareTemplate.getBreakfast());
        }
        
        if(batchCoursewareTemplate.getLunch() != null) {
            this.createDietaryProgram(batchCourseware, batchCoursewareTemplate.getLunch());
        }
        
        if(batchCoursewareTemplate.getDinner() != null) {
            this.createDietaryProgram(batchCourseware, batchCoursewareTemplate.getDinner());
        }
        
    }
    
    /**
     * 
     * @param batchCourseware
     * @param template
     */
    private void createDietaryProgram(BatchCourseware batchCourseware, DietaryProgramTemplate template) {
        DietaryProgram snacks = null;
        if(template.getSnacks() != null) {
            DietaryProgramTemplate templateSnacks = genericDaoSupport.load(DietaryProgramTemplate.class, template.getSnacks());
            snacks = new DietaryProgram(templateSnacks, batchCourseware);
            genericDaoSupport.save(snacks);
        }
        
        DietaryProgram dietaryProgram = new DietaryProgram(template, batchCourseware, snacks == null ? null : snacks.getId());
        genericDaoSupport.save(dietaryProgram);
    }
    
    /**
     * 
     * @param dailyCoursewareTemplate
     * @param dailyCourseware
     */
    private void createCourseware(DailyCoursewareTemplate dailyCoursewareTemplate, DailyCourseware dailyCourseware) {
        List<CoursewareTemplate> templates = templateDao.listCoursewareTemplate(dailyCoursewareTemplate);
        for(CoursewareTemplate template : templates) {
            Courseware courseware = new Courseware(template, dailyCourseware);
            genericDaoSupport.save(courseware);
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.GroupService#saveDiet(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, co.fitstart.admin.entity.course.BatchDietProgram)
     */
    public void saveDiets(BatchCourseware batchCourseware, BatchDietProgram batchDietProgram) {
        this.saveDiet(batchCourseware.getDietaryPrograms().get(DietaryProgramType.BREAKFAST),
                      batchDietProgram.getBreakfastDescription(),
                      batchDietProgram.getBreakfastSnacksDescription());
        
        this.saveDiet(batchCourseware.getDietaryPrograms().get(DietaryProgramType.LUNCH),
                      batchDietProgram.getLunchDescription(),
                      batchDietProgram.getLunchSnacksDescription());
        
        this.saveDiet(batchCourseware.getDietaryPrograms().get(DietaryProgramType.DINNER),
                      batchDietProgram.getDinnerDescription(),
                      batchDietProgram.getDinnerSnacksDescription());
        
        this.updateBatchStatus(batchCourseware);
    }
    
    /**
     * 
     * @param dietaryProgram
     * @param mealName
     * @param mealDescription
     * @param snacksName
     * @param snacksDescription
     */
    private void saveDiet(DietaryProgram dietaryProgram, String mealDescription, String snacksDescription) {
        genericDaoSupport.update(dietaryProgram.edit(mealDescription));
        
        if(dietaryProgram.getSnacks() != null) {
            DietaryProgram breakfastSnacks = genericDaoSupport.load(DietaryProgram.class, dietaryProgram.getSnacks());
            genericDaoSupport.update(breakfastSnacks.edit(snacksDescription));
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#createDiet(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, co.fitstart.entity.courseware.dietary.DietaryProgramTemplate)
     */
    public DietaryProgram createDiet(BatchCourseware batchCourseware, DietaryProgramTemplate dietaryProgramTemplate) {
        // if template has snacks save snacks first
//        DietaryProgram snacks = null;
//        if(dietaryProgramTemplate.getSnacks() != null) {
//            DietaryProgramTemplate snacksTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietaryProgramTemplate.getSnacks());
//            snacks = new DietaryProgram(snacksTemplate, batchCourseware);
//            genericDaoSupport.save(snacks);
//        }
        
        DietaryProgram dietaryProgram = new DietaryProgram(dietaryProgramTemplate, batchCourseware);
        genericDaoSupport.save(dietaryProgram);
        
        this.updateBatchStatus(batchCourseware);
        
        return dietaryProgram;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#createSnack(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, co.fitstart.entity.courseware.dietary.DietaryProgramTemplate, co.fitstart.entity.courseware.dietary.DietaryProgramType)
     */
    public DietaryProgram createSnack(BatchCourseware batchCourseware, DietaryProgramTemplate dietaryProgramTemplate,
            DietaryProgramType type) {
        DietaryProgram snacks = new DietaryProgram(dietaryProgramTemplate, batchCourseware);
        genericDaoSupport.save(snacks);
        
        // update diet
        DietaryProgram dietaryProgram = coursewareDao.loadDietaryProgram(batchCourseware, type);
        genericDaoSupport.update(dietaryProgram.editSnacks(snacks.getId()));
        
        this.updateBatchStatus(batchCourseware);
        
        return snacks;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#editDiet(co.fitstart.entity.courseware.dietaryProgram.DietaryProgram, co.fitstart.entity.courseware.dietaryProgram.DietaryProgramTemplate)
     */
    public DietaryProgram editDiet(DietaryProgram dietaryProgram, DietaryProgramTemplate template) {
        
        genericDaoSupport.update(dietaryProgram.edit(template));

        this.updateBatchStatus(dietaryProgram.getBatch());
        
        return dietaryProgram;
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#editCoursewareTags(java.util.List, java.util.List, java.util.List)
     */
    public void editCoursewareTags(List<Long> ids, List<String> names, List<String> descriptions) {
        for(int i=0; i<ids.size(); i++) {
            CoursewareTag coursewareTag = genericDaoSupport.load(CoursewareTag.class, ids.get(i));
            genericDaoSupport.update(coursewareTag.edit(names.get(i), descriptions.get(i)));
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.GroupService#rest(co.fitstart.entity.courseware.fitnessgroup.DailyCourseware)
     */
    public void rest(DailyCourseware dailyCourseware) {
        // if this dailyCourseware have coursewares delete these coursewares
        coursewareDao.deleteCourseware(dailyCourseware);
        
        // update dailyCourseware
        genericDaoSupport.update(dailyCourseware.changeType(DailyCoursewareType.REST));
        
        this.updateDailyStatus(dailyCourseware);
        
        this.updateBatchStatus(dailyCourseware.getBatch());
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.GroupService#createCourseware(co.fitstart.entity.courseware.fitnessgroup.DailyCourseware, co.fitstart.entity.courseware.fitnessgroup.Courseware, co.fitstart.entity.courseware.fitnessgroup.CoursewareMovementType)
     */
    public void createCourseware(DailyCourseware dailyCourseware, Courseware courseware, CoursewareMovementType movementType) {
        if(dailyCourseware.getType() == null || dailyCourseware.getType().isRest()) {
            genericDaoSupport.update(dailyCourseware.changeType(DailyCoursewareType.TRAINING));
        }
        
        genericDaoSupport.save(courseware.initilization(dailyCourseware, movementType));
        
        this.updateDailyStatus(dailyCourseware);
        
        this.updateBatchStatus(dailyCourseware.getBatch());
        
    }
    
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#createCourseware(co.fitstart.entity.course.schedule.CourseSchedule, co.fitstart.entity.courseware.fitnessgroup.Courseware)
     */
//    public void createCourseware(CourseSchedule courseSchedule, Courseware courseware) {
//        genericDaoSupport.save(courseware.initilization(courseSchedule));
//    }
    
    public void createCourseware(CourseSchedule courseSchedule, Long[] movementIds) {
        if(movementIds != null && movementIds.length > 0) {
            for(Long movementId : movementIds) {
                Movement movement = genericDaoSupport.load(Movement.class, movementId);
                
                if(movement == null) {
                    throw new BusinessException("不存在的动作");
                } else {
                    Courseware courseware = new Courseware(courseSchedule, movement);
                    genericDaoSupport.save(courseware);
                }
            }
        }
    }
    
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#createCourseware(co.fitstart.entity.course.schedule.CourseSchedule, co.fitstart.entity.courseware.fitnessgroup.Courseware, java.lang.String)
     */
    public void createCourseware(CourseSchedule courseSchedule, String name) {
        Movement movement = this.createMovement(courseSchedule.getCoach(), name);
        
        Courseware courseware = new Courseware(courseSchedule, movement);
        genericDaoSupport.save(courseware);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#createCoursewareGroup(co.fitstart.entity.course.schedule.CourseSchedule, co.fitstart.entity.courseware.fitnessgroup.FitnessGroup)
     */
    public void createCoursewareGroup(CourseSchedule courseSchedule, FitnessGroup fitnessGroup) {
        // list this group's dailyCourseware template
        SpecialCoursewareTemplate specialCoursewareTemplate = templateDao.loadSpecialCoursewareTemplate(fitnessGroup);
        if(specialCoursewareTemplate != null) {
            List<CoursewareTemplate> coursewareTemplates = templateDao.listCoursewareTemplate(specialCoursewareTemplate);
            
            // create courseware template
            for(CoursewareTemplate template : coursewareTemplates) {
                Courseware courseware = new Courseware(courseSchedule, template);
                genericDaoSupport.save(courseware);
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#createCoursewareGroup(co.fitstart.entity.courseware.fitnessgroup.DailyCourseware, co.fitstart.entity.courseware.fitnessgroup.FitnessGroup, co.fitstart.entity.courseware.fitnessgroup.CoursewareMovementType)
     */
    public void createCoursewareGroup(DailyCourseware dailyCourseware, FitnessGroup fitnessGroup,
            CoursewareMovementType type) {
        
        if(dailyCourseware.getType() == null || dailyCourseware.getType().isRest()) {
            genericDaoSupport.update(dailyCourseware.changeType(DailyCoursewareType.TRAINING));
        }
        
        // list this group's dailyCourseware template
        SpecialCoursewareTemplate specialCoursewareTemplate = templateDao.loadSpecialCoursewareTemplate(fitnessGroup);
        if(specialCoursewareTemplate != null) {
            List<CoursewareTemplate> coursewareTemplates = templateDao.listCoursewareTemplate(specialCoursewareTemplate);
            
            // create courseware template
            for(CoursewareTemplate template : coursewareTemplates) {
                Courseware courseware = new Courseware(template, dailyCourseware, type);
                genericDaoSupport.save(courseware);
            }
            
        }
        
        this.updateDailyStatus(dailyCourseware);
        
        this.updateBatchStatus(dailyCourseware.getBatch());
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.GroupService#editCourseware(co.fitstart.entity.courseware.fitnessgroup.Courseware)
     */
    public void editCourseware(Courseware courseware) {
        genericDaoSupport.update(courseware);
        
        if(courseware.getDaily() != null) {
            this.updateDailyStatus(courseware.getDaily());
            
            if(courseware.getDaily().getBatch() != null) {
                this.updateBatchStatus(courseware.getDaily().getBatch());
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.GroupService#deleteCourseware(co.fitstart.entity.courseware.fitnessgroup.Courseware)
     */
    public void deleteCourseware(Courseware courseware) {
        genericDaoSupport.update(courseware.disable());
        
        this.updateDailyStatus(courseware.getDaily());
        
        this.updateBatchStatus(courseware.getDaily().getBatch());
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#linkBatchCourseware(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, java.lang.String)
     */
     public void linkBatchCourseware(BatchCourseware batchCourseware, String coachTips) {
         genericDaoSupport.update(batchCourseware.link("batch_courseware_link", coachTips));
         
         coursewareDao.updateDailyStatus(batchCourseware);
         
         // send message to student
         smsSender.sendCoursewareNotice(batchCourseware.getCourseRound().getStudent().getMobile());
         
         this.finishCourseRound(batchCourseware.getCourseRound(), "weekly", 1);
     }
     
     /*
      * (non-Javadoc)
      * @see co.fitstart.coach.service.CoursewareService#linkDailyCourseware(co.fitstart.entity.courseware.fitnessgroup.DailyCourseware, java.lang.String)
      */
     public void linkDailyCourseware(DailyCourseware dailyCourseware, String coachTips) {
         
         // 1.update dailyCourseware
         genericDaoSupport.update(dailyCourseware.link(coachTips));
         
         // 2. if all dailyCourseware is finish update batch
         BatchCourseware batchCourseware = dailyCourseware.getBatch();
         List<DailyCourseware> dailyCoursewares = coursewareDao.listDailyCourseware(batchCourseware);
         if(dailyCoursewares.isEmpty()) {
             genericDaoSupport.update(batchCourseware.link(coachTips));
         }
         
         // 3. send message to student
         smsSender.sendCoursewareNotice(batchCourseware.getCourseRound().getStudent().getMobile());
         
         // 4. finish CourseRound's courseware
         this.finishCourseRound(batchCourseware.getCourseRound(), "course", 1);
     }
     
    /**
     * 
     * @param batchCourseware
     */
    private void updateBatchStatus(BatchCourseware batchCourseware) {
        if(batchCourseware.getEditStatus().isPrepare()) {
            genericDaoSupport.update(batchCourseware.editBatch());
        }
    }
    
    /**
     * 
     * @param dailyCourseware
     */
    private void updateDailyStatus(DailyCourseware dailyCourseware) {
        if(dailyCourseware.getEditStatus().isPrepare()) {
            genericDaoSupport.update(dailyCourseware.editDaily());
        }
    }
    
    /**
     * 
     * @param courseRound
     * @param type
     * @param amount
     */
    private void finishCourseRound(CourseRound courseRound, String type, int amount) {
        courseRound.editPlannedAmount(type, amount);
        
        List<BatchCourseware> batchCoursewares = coursewareDao.listBatchCourseware(courseRound);
        if(batchCoursewares.isEmpty()) {
            courseRound.finishCourseware();
        }
        
        genericDaoSupport.update(courseRound);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#searchMovement(co.fitstart.coach.entity.courseware.MovementSearch)
     */
    @SuppressWarnings("unchecked")
    public List<Movement> searchMovement(MovementSearch movementSearch) {
        return genericDaoSupport.searchForList(movementSearch.buildSentenceTemplate(), movementSearch.buildParameters());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#listCourseCourseware(co.fitstart.entity.courseware.fitnessgroup.DailyCourseware)
     */
    public List<Courseware> listCourseCourseware(DailyCourseware dailyCourseware) {
        return coursewareDao.listCourseware(dailyCourseware);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#deleteCourseware(co.fitstart.entity.course.schedule.CourseSchedule, java.lang.Long[])
     */
    public void deleteCourseware(CourseSchedule courseSchedule, Long[] coursewareIds) {
        if(coursewareIds != null && coursewareIds.length > 0) {
            for(Long coursewareId : coursewareIds) {
                Courseware courseware = genericDaoSupport.load(Courseware.class, coursewareId);
                
                if(courseware != null && courseware.getCourseSchedule() == courseSchedule) {
                    genericDaoSupport.save(courseware.disable());
                } else {
                    throw new BusinessException("删除动作失败");
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#listCourseWares(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public List<Courseware> listCourseWares(CourseSchedule courseSchedule) {
        return coursewareDao.listCourseware(courseSchedule);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#getRank(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public int getRank(CourseSchedule courseSchedule) {
        return coursewareDao.getRank(courseSchedule.getCourseRound(), new ScheduleStatus[]{ScheduleStatus.READY, ScheduleStatus.EXPIRY, ScheduleStatus.FINISH}, courseSchedule.getOccurDate(), courseSchedule.getOccurHour());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#listCourseSchedule(co.fitstart.entity.course.round.CourseRound)
     */
    public List<CourseSchedule> listCourseSchedule(CourseRound courseRound) {
        return courseDao.listCourseSchedule(courseRound, new ScheduleStatus[]{ScheduleStatus.READY, ScheduleStatus.FINISH, ScheduleStatus.EXPIRY});
    }
    
    /**
     * 
     * @param coach
     * @param name
     * @return
     */
    private Movement createMovement(Principal coach, String name) {
        Movement movement = coursewareDao.getMovementByName(name);
        
        if(movement == null) {
            movement = new Movement(name, false);
            
            genericDaoSupport.save(movement.initialize(coach));
        }
        
        return movement;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#editCourseware(co.fitstart.entity.course.schedule.CourseSchedule, java.util.List)
     */
    public void editCourseware(CourseSchedule courseSchedule, List<Courseware> coursewares) {
        for(Courseware courseware : coursewares) {
            Courseware originalCourseware = this.loadCourseware(courseware.getId());
            
            if(originalCourseware == null || originalCourseware.getCourseSchedule() != courseSchedule) {
                throw new BusinessException("修改动作失败");
            } else {
                genericDaoSupport.update(originalCourseware.edit(courseware.getGroupAmount(), courseware.getMovementNumber(), courseware.getMaxWeight(), courseware.getTime()));
            }
        }
    }
    
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoursewareService#listMovement(java.util.List)
     */
    public List<Movement> listMovement(List<ExerciseContent> contents) {
        Set<String> categorys = new HashSet<>();
        
        for(ExerciseContent content : contents) {
            categorys.add(content.getDescription() + "部训练");
        }
        
        return coursewareDao.listMovement(categorys);
    }
}
